package org.jchains.intercept;

import java.security.*;
import java.util.Hashtable;

// Referenced classes of package org.illegalaccess.jchains.intercept:
//            TextEmitter, Emitter

public class JChainsSecInterceptor extends SecurityManager {
	private static final String str_CommonPackagePrefix = "org.jchains";
	private static final String emitterprefix = "org.jchains.intercept.";
	private static final String str_emitClassName = "org.jchains.emitClass";
	private static boolean alwaysemit = Boolean
			.getBoolean("org.jchains.always");

	ThreadGroup tg = new ThreadGroup("jchains");
	static Emitter emit;
	// There is only one emitter useful for a SecInterceptor
	static final java.util.logging.Logger log = java.util.logging.Logger
			.getLogger("JChains");

	static {
		System.out.println("choosing emitter");
		emit = new TextEmitter();
		String s = emitterprefix
				+ System.getProperty(str_emitClassName, "StandardEmitter");
		try {

			if (s != null && s.length() > 0)
				emit = (Emitter) Class.forName(s).newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			try {
				emit.init();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		System.out.println(emit + " inited");
	}

	ProtectionDomain pd_default = (java.lang.String.class)
			.getProtectionDomain();
	PermissionCollection pc_default = pd_default.getPermissions();

	// static int sem; semaphor, unused til now

	class OrderedProtectionDomain {

		short m_number;
		ProtectionDomain m_pd;

		OrderedProtectionDomain(short i, ProtectionDomain pd) {
			m_number = i;
			m_pd = pd;
		}
	}

	class CachedPDCheck extends Hashtable<Class, Boolean> {

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.util.Hashtable#put(java.lang.Object, java.lang.Object)
		 */
		public synchronized Boolean putClass(Class key) {
			// TODO Auto-generated method stub
			
			Boolean ret;
			CodeSource cs = null;
			if (!containsKey(key)) {
				// CodeSource cs = get(key);
				// if (cs == null) {
				cs = getCodeSourceForClass(key);
				ret = cs != null;
				super.put(key, ret);
				
			}

			else
				ret = get(key);

			return ret;

		}

	}

	CachedPDCheck cpc = new CachedPDCheck();

	public JChainsSecInterceptor() {
		System.out.println("add JChainsSecInterceptor ShutdownHook");
		// sem = 0;
		Runtime.getRuntime().addShutdownHook(
				new JChainsSecInterceptor.InterceptorShutdown());

		// Vector permcache = new Vector();
	}

	public void checkPermission(Permission permission) {

//		boolean cache = true;

		Class[] aclass = getClassContext();
		int i = 0;
		for (int j = 0; j < aclass.length; j++) {

			// System.err.println(j + ":" + aclass[j].getName()); //boese falle
			if (aclass[j].getName().startsWith(str_CommonPackagePrefix)
					&& ++i > 1) {
				// System.out.println("************search abort*******");
				return;
				// wenn wir mindestens zwei mal im Stack Trace sind, dann rufen
				// wir uns selbst auf
				// gefunden bei Test mit Tomcat 5.0.16, da gab es einen
				// StackOverflow
			}
		}
		SecurityException theException = null;
		int allowed = 1;
		if (!alwaysemit) {

			try {
				super.checkPermission(permission);
			} catch (java.lang.SecurityException sp) {
				allowed = 0;
				theException = sp;
			}
		}
		// theException.printStackTrace();

		// System.out.println(allowed+":"+permission);

		// if (theException == null) {
		// theException = new SecurityException();
		// }
		if (allowed == 0 || alwaysemit) {
			theException = (theException == null) ? new SecurityException()
					: theException;

			StackTraceElement astacktraceelement[] = theException
					.getStackTrace();
			logit(aclass, astacktraceelement, permission, null);
		}
	}

	public void checkSecurityAccess(String target) {
		log.info("xxx123xxx:" + target);
	}

	public ThreadGroup getThreadGroup() {
		return tg;
	}

	public void checkPermission(Permission permission, Object obj) {
		log.info("mit OBject:" + obj);
		int allowed = 1;
		try {
			super.checkPermission(permission, obj);
		} catch (java.lang.SecurityException sp) {
			allowed = 0;
		}
		if (allowed == 0 || alwaysemit) {
			Class aclass[] = getClassContext();
			int i = 0;
			for (int j = 0; j < aclass.length; j++)
				if (aclass[j].getName().startsWith(str_CommonPackagePrefix)
						&& ++i > 1)
					return;
			StackTraceElement astacktraceelement[] = (new Throwable())
					.getStackTrace();
			logit(aclass, astacktraceelement, permission, obj);
		}
	}

	private void logit(Class classes[], StackTraceElement ste[],
			Permission permission, Object obj) {
		// System.out.println("classes:"+classes.length);
		// System.out.println("ste:"+classes.length);

		// System.out.println("***" + classes.length + ":" + ste.length +
		// "***");

		// System.out.println(ste[ste.length-1].getClassName()+":"+ste[ste.length-1].getMethodName());

		// for (int i = 0; i < classes.length; i++) {
		for (int i = 0; i < classes.length; i++) {

			Class class1 = classes[i];
			String sname = class1.getName();
			// System.out.println("Class:"+i+":"+class1.getName());
			if (!sname.startsWith(str_CommonPackagePrefix)
					&& !sname.startsWith("sun.") && !sname.startsWith("java.")
					&& !sname.startsWith("javax.")) {

				// System.out.println(class1.getName());

				// CodeSource codesource = getCodeSourceForClass(class1);
				Boolean ret = cpc.putClass(class1);

				// String s1 = "<ALL>";
				if (ret) {
					emit.emit(class1, permission,/* 0, */ste);
				}
			}
			// URL u = codesource.getLocation();
			// String s2 = (u == null ? "" : u.toString());
			// OrderedProtectionDomain odp =
			// new OrderedProtectionDomain(
			// (short) i,
			// protectiondomain);
			// hashtable.put(sname, odp); // oder doch s2
			// } else {
			//
			// }
			// }
		}

		// for (Enumeration enumeration = hashtable.keys();
		// enumeration.hasMoreElements();
		// ) {
		// String s = (String) enumeration.nextElement();
		// OrderedProtectionDomain opd =
		// (OrderedProtectionDomain) hashtable.get(s);
		// ProtectionDomain protectiondomain1 = opd.m_pd;
		// emit.emit(protectiondomain1, permission, opd.m_number, ste);
		// }
	}

	private CodeSource getCodeSourceForClass(Class class1) {
		CodeSource codesource = null;
		ProtectionDomain protectiondomain = class1.getProtectionDomain();
		if (protectiondomain != null) {
			// System.out.println("protectiondomain:"+protectiondomain);
			codesource = protectiondomain.getCodeSource();
		}
		return codesource;
	}

	protected void finalize() {
		emit.exit();
	}

	class InterceptorShutdown extends Thread {

		int sid;

		InterceptorShutdown() {
			// sid = id;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Runnable#run()
		 */
		public void run() {
			System.out.println("JChainsSecInterceptor ShutdownHook called");
			emit.exit();

		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.Hashtable#put(java.lang.Object, java.lang.Object)
	 */

}
